package org.qiyu.live.user.provider.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.idea.qiyu.live.framework.redis.starter.key.UserProviderCacheKeyBuilder;
import org.qiyu.live.common.interfaces.utils.DESUtils;
import org.qiyu.live.id.generate.enums.IdTypeEnum;
import org.qiyu.live.id.generate.interfaces.IdGenerateRpc;
import org.qiyu.live.user.dto.UserDTO;
import org.qiyu.live.user.dto.UserLoginDTO;
import org.qiyu.live.user.dto.UserPhoneDTO;
import org.qiyu.live.user.provider.dao.mapper.IUserMapper;
import org.qiyu.live.user.provider.dao.mapper.IUserPhoneMapper;
import org.qiyu.live.user.provider.dao.po.UserPO;
import org.qiyu.live.user.provider.dao.po.UserPhonePO;
import org.qiyu.live.user.provider.service.IUserPhoneService;
import org.qiyu.live.common.interfaces.enums.CommonStatusEnum;
import org.qiyu.live.common.interfaces.utils.ConvertBeanUtils;
import org.qiyu.live.user.provider.service.IUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: sfx
 * Date: 2023-10-01
 * Time: 20:57
 */
@Service
public class UserPhoneServiceImpl implements IUserPhoneService {

    private final Logger LOGGER = LoggerFactory.getLogger(UserPhoneServiceImpl.class);

    @Resource
    private IUserPhoneMapper userPhoneMapper;

    @Resource
    private IUserService userService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private UserProviderCacheKeyBuilder userProviderCacheKeyBuilder;

    @DubboReference
    private IdGenerateRpc idGenerateRpc;

    /**
     * 如果用户没有注册,则先进行注册,否则直接登陆即可
     *
     * @param phone 手机号
     * @return 返回用户登陆信息
     */
    @Override
    public UserLoginDTO login(String phone) {
        // 1. 校验手机号是否合法
        if (StringUtils.isEmpty(phone)) {
            LOGGER.error("手机号不合法 phone is {}", phone);
            return UserLoginDTO.loginError("手机号不合法");
        }
        // 2. 根据手机号查询用户信息
        UserPhoneDTO userPhoneDTO = this.queryByPhone(phone);
        // 3. 用户存在,则登陆成功,返回 userId
        if (userPhoneDTO != null) {
            return UserLoginDTO.loginSuccess(userPhoneDTO.getUserId());
        }
        // 4. 用户不存在,则进行注册
        return registerUserPhoneDTO(phone);
    }

    /**
     * 注册用户
     *
     * @param phone 手机号
     * @return
     */
    private UserLoginDTO registerUserPhoneDTO(String phone) {
        UserPhonePO userPhonePO = new UserPhonePO();
        Long userId = idGenerateRpc.getUnSeqId(IdTypeEnum.USER_ID.getCode());
        userPhonePO.setUserId(userId); // 生成用户id
        userPhonePO.setPhone(DESUtils.encrypt(phone));
        userPhonePO.setStatus(CommonStatusEnum.VALID.getCode());
        userPhoneMapper.insert(userPhonePO);
        UserDTO userDTO = new UserDTO();
        userDTO.setUserId(userId);
        userDTO.setNickName("qiyu-user-" + userId);
        userService.insertOne(userDTO);
        // 注册之后,我们需要把空值缓存给删除,防止再次查询用户为空值
        String key = userProviderCacheKeyBuilder.buildUserPhoneKey(phone);
        redisTemplate.delete(key);
        return UserLoginDTO.loginSuccess(userPhonePO.getUserId());
    }


    /**
     * 根据手机号查询用户信息
     * @param phone 手机号
     * @return
     */
    @Override
    public UserPhoneDTO queryByPhone(String phone) {
        // 1. 先查询redis
        String key = userProviderCacheKeyBuilder.buildUserPhoneKey(phone);
        UserPhoneDTO userPhoneDTO = (UserPhoneDTO) redisTemplate.opsForValue().get(key);
        // 1.1 redis存在直接返回
        if (userPhoneDTO != null) {
            if (userPhoneDTO.getUserId() == null) {
                // 命中空值缓存
                return null;
            }
            return userPhoneDTO;
        }
        // 2.redis不存在直接查询数据库
        userPhoneDTO = this.queryByPhoneForDb(phone);
        if (userPhoneDTO != null) {
            // 2.1 数据库存在,缓存到redis
            redisTemplate.opsForValue().set(key, userPhoneDTO, 30, TimeUnit.MINUTES);
            return userPhoneDTO;
        }
        // 我们这里要解决缓存击穿问题(查询redis不在,db也不再,我们简单地解决使用缓存空对象)
        redisTemplate.opsForValue().set(key, new UserPhoneDTO(), 5, TimeUnit.MINUTES);
        // 数据库不存在,直接诶返回null
        return null;
    }

    public UserPhoneDTO queryByPhoneForDb(String phone) {
        LambdaQueryWrapper<UserPhonePO> queryWrapper = new LambdaQueryWrapper<>();
        // 查询的时候按照加密查询
        queryWrapper.eq(UserPhonePO::getPhone, DESUtils.encrypt(phone));
        queryWrapper.eq(UserPhonePO::getStatus, CommonStatusEnum.VALID.getCode());
        queryWrapper.last("limit 1");
        return ConvertBeanUtils.convert(userPhoneMapper.selectOne(queryWrapper), UserPhoneDTO.class);
    }

    /**
     * 根据userId查询多个手机号
     *
     * @param userId userId
     * @return 返回多个用户
     */
    @Override
    public List<UserPhoneDTO> queryByUserId(Long userId) {
        // 1. 检查userId的合法性,不合法返回空集合
        if (userId == null) {
            // 直接返回一个空集合
            return Collections.emptyList();
        }

        // 2. 查询redis,redis存在直接返回
        String key = userProviderCacheKeyBuilder.builderUserPhoneListKey(userId);
        List<Object> userPhoneDTOList = redisTemplate.opsForList().range(key, 0, -1);
        if (!CollectionUtils.isEmpty(userPhoneDTOList)) {
            // 命中缓存空对象
            if (((UserPhoneDTO) userPhoneDTOList.get(0)).getUserId() == null) {
                return Collections.emptyList();
            }
            // redis不为空直接返回
            return userPhoneDTOList.stream().map(x -> (UserPhoneDTO) x).collect(Collectors.toList());
        }

        // 3. redis不存在,查询数据库
        List<UserPhoneDTO> phoneDTOList = this.queryByUserIdFromDb(userId);
        if (phoneDTOList != null && !CollectionUtils.isEmpty(phoneDTOList)) {
            // 4. 数据库存在,缓存到redis
            // 解密放进redis中
            phoneDTOList.forEach(x -> x.setPhone(DESUtils.decrypt(x.getPhone())));
            redisTemplate.opsForList().leftPushAll(key, phoneDTOList.toArray());
            redisTemplate.expire(key, 30, TimeUnit.MINUTES);
            return phoneDTOList;
        }

        // 解决缓存击穿,我们缓存空集合
        redisTemplate.opsForList().leftPush(key, new UserPhoneDTO());
        redisTemplate.expire(key, 5, TimeUnit.MINUTES);
        // 5. 数据库不存在,返回null
        return Collections.emptyList();
    }

    public List<UserPhoneDTO> queryByUserIdFromDb(Long userId) {
        LambdaQueryWrapper<UserPhonePO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPhonePO::getUserId, userId);
        queryWrapper.eq(UserPhonePO::getStatus, CommonStatusEnum.VALID.getCode());
        queryWrapper.last("limit 1");
        return ConvertBeanUtils.convertList(userPhoneMapper.selectList(queryWrapper), UserPhoneDTO.class);
    }
}